// Filename: geomVertexWriter.I
// Created by:  drose (25Mar05)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::Default Constructor
//       Access: Published
//  Description: Constructs an invalid GeomVertexWriter.  You must use
//               the assignment operator to assign a valid
//               GeomVertexWriter to this object before you can use
//               it.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexWriter::
GeomVertexWriter(Thread *current_thread) :
  _vertex_data(NULL),
  _current_thread(current_thread)
{
  initialize();
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::Constructor
//       Access: Published
//  Description: Constructs a new writer to process the vertices of
//               the indicated data object.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexWriter::
GeomVertexWriter(GeomVertexData *vertex_data, Thread *current_thread) :
  _vertex_data(vertex_data),
  _current_thread(current_thread)
{
  initialize();
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::Constructor
//       Access: Published
//  Description: Constructs a new writer to process the vertices of
//               the indicated data object.  This flavor creates the
//               writer specifically to process the named data type.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexWriter::
GeomVertexWriter(GeomVertexData *vertex_data, CPT_InternalName name,
                 Thread *current_thread) :
  _vertex_data(vertex_data),
  _current_thread(current_thread)
{
  initialize();
  set_column(MOVE(name));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::Constructor
//       Access: Published
//  Description: Constructs a new writer to process the vertices of
//               the indicated array only.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexWriter::
GeomVertexWriter(GeomVertexArrayData *array_data,
                 Thread *current_thread) :
  _array_data(array_data),
  _current_thread(current_thread)
{
  initialize();
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::Constructor
//       Access: Published
//  Description: Constructs a new writer to process the vertices of
//               the indicated array only.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexWriter::
GeomVertexWriter(GeomVertexArrayData *array_data, int column,
                 Thread *current_thread) :
  _array_data(array_data),
  _current_thread(current_thread)
{
  initialize();
  set_column(column);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::Constructor
//       Access: Public
//  Description: Constructs a new writer to process the vertices of
//               the indicated data object.  This flavor creates the
//               writer specifically to process the named data type.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexWriter::
GeomVertexWriter(GeomVertexDataPipelineWriter *data_writer,
                 const InternalName *name) :
  _vertex_data(data_writer->get_object()),
  _current_thread(data_writer->get_current_thread())
{
  initialize();
  const GeomVertexFormat *format = data_writer->get_format();
  set_vertex_column(format->get_array_with(name),
                    format->get_column(name),
                    data_writer);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::Copy Constructor
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
INLINE GeomVertexWriter::
GeomVertexWriter(const GeomVertexWriter &copy) :
  _vertex_data(copy._vertex_data),
  _array(copy._array),
  _array_data(copy._array_data),
  _current_thread(copy._current_thread),
  _packer(copy._packer),
  _stride(copy._stride),
  _handle(copy._handle),
  _pointer_begin(copy._pointer_begin),
  _pointer_end(copy._pointer_end),
  _pointer(copy._pointer),
  _start_row(copy._start_row)
{
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::Copy Assignment Operator
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
operator = (const GeomVertexWriter &copy) {
  _vertex_data = copy._vertex_data;
  _array = copy._array;
  _array_data = copy._array_data;
  _current_thread = copy._current_thread;
  _packer = copy._packer;
  _stride = copy._stride;
  _handle = copy._handle;
  _pointer_begin = copy._pointer_begin;
  _pointer_end = copy._pointer_end;
  _pointer = copy._pointer;
  _start_row = copy._start_row;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::Destructor
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
INLINE GeomVertexWriter::
~GeomVertexWriter() {
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_vertex_data
//       Access: Published
//  Description: Returns the vertex data object that the
//               writer is processing.  This may return NULL if the
//               writer was constructed with just an array pointer.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexData *GeomVertexWriter::
get_vertex_data() const {
  return _vertex_data;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_array_data
//       Access: Published
//  Description: Returns the particular array object that the
//               writer is currently processing.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexArrayData *GeomVertexWriter::
get_array_data() const {
  return _array_data;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_array_handle
//       Access: Published
//  Description: Returns the write handle to the array object that the
//               writer is currently processing.  This low-level call
//               should be used with caution; be careful with
//               modifying the data in the handle out from under the
//               GeomVertexWriter.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexArrayDataHandle *GeomVertexWriter::
get_array_handle() const {
  return _handle;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_stride
//       Access: Published
//  Description: Returns the per-row stride (bytes between consecutive
//               rows) of the underlying vertex array.  This low-level
//               information is normally not needed to use the
//               GeomVertexWriter directly.
////////////////////////////////////////////////////////////////////
INLINE size_t GeomVertexWriter::
get_stride() const {
  return _stride;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_current_thread
//       Access: Published
//  Description: Returns the Thread pointer of the currently-executing
//               thread, as passed to the constructor of this object.
////////////////////////////////////////////////////////////////////
INLINE Thread *GeomVertexWriter::
get_current_thread() const {
  return _current_thread;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_column
//       Access: Published
//  Description: Sets up the writer to use the nth data type of the
//               GeomVertexFormat, numbering from 0.
//
//               This also resets the write row number to the start
//               row (the same value passed to a previous call to
//               set_row(), or 0 if set_row() was never called.)
//
//               The return value is true if the data type is valid,
//               false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool GeomVertexWriter::
set_column(int column) {
  if (_vertex_data != (GeomVertexData *)NULL) {
    GeomVertexDataPipelineWriter writer(_vertex_data, true, _current_thread);
    writer.check_array_writers();
    const GeomVertexFormat *format = writer.get_format();
    return set_vertex_column(format->get_array_with(column),
                             format->get_column(column),
                             &writer);
  }
  if (_array_data != (GeomVertexArrayData *)NULL) {
    return set_array_column(_array_data->get_array_format()->get_column(column));
  }
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_column
//       Access: Published
//  Description: Sets up the writer to use the data type with the
//               indicated name.
//
//               This also resets the write number to the start row
//               (the same value passed to a previous call to
//               set_row(), or 0 if set_row() was never called.)
//
//               The return value is true if the data type is valid,
//               false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool GeomVertexWriter::
set_column(CPT_InternalName name) {
  if (_vertex_data != (GeomVertexData *)NULL) {
    GeomVertexDataPipelineWriter writer(_vertex_data, true, _current_thread);
    writer.check_array_writers();
    const GeomVertexFormat *format = writer.get_format();
    return set_vertex_column(format->get_array_with(name),
                             format->get_column(name),
                             &writer);
  }
  if (_array_data != (GeomVertexArrayData *)NULL) {
    return set_array_column(_array_data->get_array_format()->get_column(name));
  }
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::clear
//       Access: Published
//  Description: Resets the GeomVertexWriter to the initial state.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
clear() {
  (*this) = GeomVertexWriter(_current_thread);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::has_column
//       Access: Published
//  Description: Returns true if a valid data type has been
//               successfully set, or false if the data type does not
//               exist.
////////////////////////////////////////////////////////////////////
INLINE bool GeomVertexWriter::
has_column() const {
  return (_packer != (GeomVertexColumn::Packer *)NULL);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_array
//       Access: Published
//  Description: Returns the array index containing the data type that
//               the writer is working on.
////////////////////////////////////////////////////////////////////
INLINE int GeomVertexWriter::
get_array() const {
  return _array;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_column
//       Access: Published
//  Description: Returns the description of the data type that the
//               writer is working on.
////////////////////////////////////////////////////////////////////
INLINE const GeomVertexColumn *GeomVertexWriter::
get_column() const {
  if (_packer != (GeomVertexColumn::Packer *)NULL) {
    return _packer->_column;
  }
  return NULL;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_row_unsafe
//       Access: Published
//  Description: Sets the start row to the indicated value, without
//               internal checks.  This is the same as set_row(), but
//               it does not check for the possibility that the array
//               has been reallocated internally for some reason; use
//               only when you are confident that the array is
//               unchanged and you really need every bit of available
//               performance.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_row_unsafe(int row) {
  _start_row = row;
  if (has_column()) {
    quick_set_pointer(_start_row);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_row
//       Access: Published
//  Description: Sets the start row to the indicated value.  The
//               writer will begin writing to the indicated row;
//               each subsequent set_data*() call will store the data
//               into the subsequent row.  If set_column() is called,
//               the writer will return to this row.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_row(int row) {
  _start_row = row;
  if (has_column()) {
    set_pointer(_start_row);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_start_row
//       Access: Published
//  Description: Returns the row index at which the writer
//               started.  It will return to this row if you reset
//               the current column.
////////////////////////////////////////////////////////////////////
INLINE int GeomVertexWriter::
get_start_row() const {
  return _start_row;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_write_row
//       Access: Published
//  Description: Returns the row index to which the data will be
//               written at the next call to set_data*() or
//               add_data*().
////////////////////////////////////////////////////////////////////
INLINE int GeomVertexWriter::
get_write_row() const {
  return (int)(_pointer - _pointer_begin) / _stride;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::is_at_end
//       Access: Published
//  Description: Returns true if the writer is currently at the end of
//               the list of vertices, false otherwise.  If this is
//               true, another call to set_data*() will result in a
//               crash, but another call to add_data*() will add a new
//               row.
////////////////////////////////////////////////////////////////////
INLINE bool GeomVertexWriter::
is_at_end() const {
  return _pointer >= _pointer_end;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data1f
//       Access: Published
//  Description: Sets the write row to a particular 1-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data1f(float data) {
  nassertv(has_column());
  _packer->set_data1f(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data2f
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data2f(float x, float y) {
  set_data2f(LVecBase2f(x, y));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data2f
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data2f(const LVecBase2f &data) {
  nassertv(has_column());
  _packer->set_data2f(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data3f
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data3f(float x, float y, float z) {
  set_data3f(LVecBase3f(x, y, z));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data3f
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data3f(const LVecBase3f &data) {
  nassertv(has_column());
  _packer->set_data3f(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data4f
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data4f(float x, float y, float z, float w) {
  set_data4f(LVecBase4f(x, y, z, w));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data4f
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data4f(const LVecBase4f &data) {
  nassertv(has_column());
  _packer->set_data4f(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_matrix3f
//       Access: Published
//  Description: Sets the write row to a 3-by-3 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_matrix3f(const LMatrix3f &mat) {
  nassertv(has_column() &&
           _packer->_column->get_contents() == C_matrix &&
           _packer->_column->get_num_elements() == 3);

  size_t col_stride = _packer->_column->get_element_stride();
  unsigned char *pointer = inc_pointer();

  _packer->set_data3f(pointer, mat.get_row(0));
  pointer += col_stride;
  _packer->set_data3f(pointer, mat.get_row(1));
  pointer += col_stride;
  _packer->set_data3f(pointer, mat.get_row(2));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_matrix4f
//       Access: Published
//  Description: Sets the write row to a 4-by-4 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_matrix4f(const LMatrix4f &mat) {
  nassertv(has_column() &&
           _packer->_column->get_contents() == C_matrix &&
           _packer->_column->get_num_elements() == 4);

  size_t col_stride = _packer->_column->get_element_stride();
  unsigned char *pointer = inc_pointer();

  _packer->set_data4f(pointer, mat.get_row(0));
  pointer += col_stride;
  _packer->set_data4f(pointer, mat.get_row(1));
  pointer += col_stride;
  _packer->set_data4f(pointer, mat.get_row(2));
  pointer += col_stride;
  _packer->set_data4f(pointer, mat.get_row(3));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data1d
//       Access: Published
//  Description: Sets the write row to a particular 1-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data1d(double data) {
  nassertv(has_column());
  _packer->set_data1d(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data2d
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data2d(double x, double y) {
  set_data2d(LVecBase2d(x, y));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data2d
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data2d(const LVecBase2d &data) {
  nassertv(has_column());
  _packer->set_data2d(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data3d
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data3d(double x, double y, double z) {
  set_data3d(LVecBase3d(x, y, z));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data3d
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data3d(const LVecBase3d &data) {
  nassertv(has_column());
  _packer->set_data3d(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data4d
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data4d(double x, double y, double z, double w) {
  set_data4d(LVecBase4d(x, y, z, w));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data4d
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data4d(const LVecBase4d &data) {
  nassertv(has_column());
  _packer->set_data4d(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_matrix3d
//       Access: Published
//  Description: Sets the write row to a 3-by-3 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_matrix3d(const LMatrix3d &mat) {
  nassertv(has_column() &&
           _packer->_column->get_contents() == C_matrix &&
           _packer->_column->get_num_elements() == 3);

  size_t col_stride = _packer->_column->get_element_stride();
  unsigned char *pointer = inc_pointer();

  _packer->set_data3d(pointer, mat.get_row(0));
  pointer += col_stride;
  _packer->set_data3d(pointer, mat.get_row(1));
  pointer += col_stride;
  _packer->set_data3d(pointer, mat.get_row(2));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_matrix4d
//       Access: Published
//  Description: Sets the write row to a 4-by-4 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_matrix4d(const LMatrix4d &mat) {
  nassertv(has_column() &&
           _packer->_column->get_contents() == C_matrix &&
           _packer->_column->get_num_elements() == 4);

  size_t col_stride = _packer->_column->get_element_stride();
  unsigned char *pointer = inc_pointer();

  _packer->set_data4d(pointer, mat.get_row(0));
  pointer += col_stride;
  _packer->set_data4d(pointer, mat.get_row(1));
  pointer += col_stride;
  _packer->set_data4d(pointer, mat.get_row(2));
  pointer += col_stride;
  _packer->set_data4d(pointer, mat.get_row(3));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data1
//       Access: Published
//  Description: Sets the write row to a particular 1-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data1(PN_stdfloat data) {
#ifndef STDFLOAT_DOUBLE
  set_data1f(data);
#else
  set_data1d(data);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data2
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data2(PN_stdfloat x, PN_stdfloat y) {
#ifndef STDFLOAT_DOUBLE
  set_data2f(x, y);
#else
  set_data2d(x, y);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data2
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data2(const LVecBase2 &data) {
#ifndef STDFLOAT_DOUBLE
  set_data2f(data);
#else
  set_data2d(data);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data3
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
#ifndef STDFLOAT_DOUBLE
  set_data3f(x, y, z);
#else
  set_data3d(x, y, z);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data3
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data3(const LVecBase3 &data) {
#ifndef STDFLOAT_DOUBLE
  set_data3f(data);
#else
  set_data3d(data);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data4
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data4(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat w) {
#ifndef STDFLOAT_DOUBLE
  set_data4f(x, y, z, w);
#else
  set_data4d(x, y, z, w);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data4
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data4(const LVecBase4 &data) {
#ifndef STDFLOAT_DOUBLE
  set_data4f(data);
#else
  set_data4d(data);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_matrix3
//       Access: Published
//  Description: Sets the write row to a 3-by-3 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_matrix3(const LMatrix3 &mat) {
#ifndef STDFLOAT_DOUBLE
  set_matrix3f(mat);
#else
  set_matrix3d(mat);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_matrix4
//       Access: Published
//  Description: Sets the write row to a 4-by-4 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_matrix4(const LMatrix4 &mat) {
#ifndef STDFLOAT_DOUBLE
  set_matrix4f(mat);
#else
  set_matrix4d(mat);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data1i
//       Access: Published
//  Description: Sets the write row to a particular 1-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data1i(int data) {
  nassertv(has_column());
  _packer->set_data1i(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data2i
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data2i(int a, int b) {
  set_data2i(LVecBase2i(a, b));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data2i
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data2i(const LVecBase2i &data) {
  nassertv(has_column());
  _packer->set_data2i(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data3i
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data3i(int a, int b, int c) {
  set_data3i(LVecBase3i(a, b, c));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data3i
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data3i(const LVecBase3i &data) {
  nassertv(has_column());
  _packer->set_data3i(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data4i
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data4i(int a, int b, int c, int d) {
  set_data4i(LVecBase4i(a, b, c, d));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_data4i
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               It is an error for the write row to advance past
//               the end of data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_data4i(const LVecBase4i &data) {
  nassertv(has_column());
  _packer->set_data4i(inc_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data1f
//       Access: Published
//  Description: Sets the write row to a particular 1-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data1f(float data) {
  nassertv(has_column());
  _packer->set_data1f(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data2f
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data2f(float x, float y) {
  add_data2f(LVecBase2f(x, y));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data2f
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data2f(const LVecBase2f &data) {
  nassertv(has_column());
  _packer->set_data2f(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data3f
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data3f(float x, float y, float z) {
  add_data3f(LVecBase3f(x, y, z));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data3f
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data3f(const LVecBase3f &data) {
  nassertv(has_column());
  _packer->set_data3f(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data4f
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data4f(float x, float y, float z, float w) {
  add_data4f(LVecBase4f(x, y, z, w));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data4f
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data4f(const LVecBase4f &data) {
  nassertv(has_column());
  _packer->set_data4f(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_matrix3f
//       Access: Published
//  Description: Sets the write row to a 3-by-3 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_matrix3f(const LMatrix3f &mat) {
  nassertv(has_column() &&
           _packer->_column->get_contents() == C_matrix &&
           _packer->_column->get_num_elements() == 3);

  size_t col_stride = _packer->_column->get_element_stride();
  unsigned char *pointer = inc_add_pointer();

  _packer->set_data3f(pointer, mat.get_row(0));
  pointer += col_stride;
  _packer->set_data3f(pointer, mat.get_row(1));
  pointer += col_stride;
  _packer->set_data3f(pointer, mat.get_row(2));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_matrix4f
//       Access: Published
//  Description: Sets the write row to a 4-by-4 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_matrix4f(const LMatrix4f &mat) {
  nassertv(has_column() &&
           _packer->_column->get_contents() == C_matrix &&
           _packer->_column->get_num_elements() == 4);

  size_t col_stride = _packer->_column->get_element_stride();
  unsigned char *pointer = inc_add_pointer();

  _packer->set_data4f(pointer, mat.get_row(0));
  pointer += col_stride;
  _packer->set_data4f(pointer, mat.get_row(1));
  pointer += col_stride;
  _packer->set_data4f(pointer, mat.get_row(2));
  pointer += col_stride;
  _packer->set_data4f(pointer, mat.get_row(3));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data1d
//       Access: Published
//  Description: Sets the write row to a particular 1-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data1d(double data) {
  nassertv(has_column());
  _packer->set_data1d(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data2d
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data2d(double x, double y) {
  add_data2d(LVecBase2d(x, y));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data2d
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data2d(const LVecBase2d &data) {
  nassertv(has_column());
  _packer->set_data2d(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data3d
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data3d(double x, double y, double z) {
  add_data3d(LVecBase3d(x, y, z));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data3d
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data3d(const LVecBase3d &data) {
  nassertv(has_column());
  _packer->set_data3d(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data4d
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data4d(double x, double y, double z, double w) {
  add_data4d(LVecBase4d(x, y, z, w));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data4d
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data4d(const LVecBase4d &data) {
  nassertv(has_column());
  _packer->set_data4d(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_matrix3d
//       Access: Published
//  Description: Sets the write row to a 3-by-3 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_matrix3d(const LMatrix3d &mat) {
  nassertv(has_column() &&
           _packer->_column->get_contents() == C_matrix &&
           _packer->_column->get_num_elements() == 3);

  size_t col_stride = _packer->_column->get_element_stride();
  unsigned char *pointer = inc_add_pointer();

  _packer->set_data3d(pointer, mat.get_row(0));
  pointer += col_stride;
  _packer->set_data3d(pointer, mat.get_row(1));
  pointer += col_stride;
  _packer->set_data3d(pointer, mat.get_row(2));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_matrix4d
//       Access: Published
//  Description: Sets the write row to a 4-by-4 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_matrix4d(const LMatrix4d &mat) {
  nassertv(has_column() &&
           _packer->_column->get_contents() == C_matrix &&
           _packer->_column->get_num_elements() == 4);

  size_t col_stride = _packer->_column->get_element_stride();
  unsigned char *pointer = inc_add_pointer();

  _packer->set_data4d(pointer, mat.get_row(0));
  pointer += col_stride;
  _packer->set_data4d(pointer, mat.get_row(1));
  pointer += col_stride;
  _packer->set_data4d(pointer, mat.get_row(2));
  pointer += col_stride;
  _packer->set_data4d(pointer, mat.get_row(3));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data1
//       Access: Published
//  Description: Sets the write row to a particular 1-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data1(PN_stdfloat data) {
#ifndef STDFLOAT_DOUBLE
  add_data1f(data);
#else
  add_data1d(data);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data2
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data2(PN_stdfloat x, PN_stdfloat y) {
#ifndef STDFLOAT_DOUBLE
  add_data2f(x, y);
#else
  add_data2d(x, y);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data2
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data2(const LVecBase2 &data) {
#ifndef STDFLOAT_DOUBLE
  add_data2f(data);
#else
  add_data2d(data);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data3
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
#ifndef STDFLOAT_DOUBLE
  add_data3f(x, y, z);
#else
  add_data3d(x, y, z);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data3
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data3(const LVecBase3 &data) {
#ifndef STDFLOAT_DOUBLE
  add_data3f(data);
#else
  add_data3d(data);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data4
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data4(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat w) {
#ifndef STDFLOAT_DOUBLE
  add_data4f(x, y, z, w);
#else
  add_data4d(x, y, z, w);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data4
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data4(const LVecBase4 &data) {
#ifndef STDFLOAT_DOUBLE
  add_data4f(data);
#else
  add_data4d(data);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_matrix3
//       Access: Published
//  Description: Sets the write row to a 3-by-3 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_matrix3(const LMatrix3 &mat) {
#ifndef STDFLOAT_DOUBLE
  add_matrix3f(mat);
#else
  add_matrix3d(mat);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_matrix4
//       Access: Published
//  Description: Sets the write row to a 4-by-4 matrix, and advances
//               the write row.  This is a special method that can
//               only be used on matrix columns.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_matrix4(const LMatrix4 &mat) {
#ifndef STDFLOAT_DOUBLE
  add_matrix4f(mat);
#else
  add_matrix4d(mat);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data1i
//       Access: Published
//  Description: Sets the write row to a particular 1-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data1i(int data) {
  nassertv(has_column());
  _packer->set_data1i(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data2i
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data2i(int a, int b) {
  add_data2i(LVecBase2i(a, b));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data2i
//       Access: Published
//  Description: Sets the write row to a particular 2-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data2i(const LVecBase2i &data) {
  nassertv(has_column());
  _packer->set_data2i(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data3i
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data3i(int a, int b, int c) {
  add_data3i(LVecBase3i(a, b, c));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data3i
//       Access: Published
//  Description: Sets the write row to a particular 3-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data3i(const LVecBase3i &data) {
  nassertv(has_column());
  _packer->set_data3i(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data4i
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data4i(int a, int b, int c, int d) {
  add_data4i(LVecBase4i(a, b, c, d));
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::add_data4i
//       Access: Published
//  Description: Sets the write row to a particular 4-component
//               value, and advances the write row.
//
//               If the write row advances past the end of data,
//               implicitly adds a new row to the data.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
add_data4i(const LVecBase4i &data) {
  nassertv(has_column());
  _packer->set_data4i(inc_add_pointer(), data);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::get_packer
//       Access: Protected
//  Description: Returns the writer's Packer object.
////////////////////////////////////////////////////////////////////
INLINE GeomVertexColumn::Packer *GeomVertexWriter::
get_packer() const {
  return _packer;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::set_pointer
//       Access: Private
//  Description: Sets up the array pointers freshly from the source
//               object (in case they have been reallocated recently),
//               and sets the internal pointer to the indicated row.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
set_pointer(int row) {
  _pointer_begin = _handle->get_write_pointer();
  _pointer_end = _pointer_begin + _handle->get_data_size_bytes();
  quick_set_pointer(row);
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::quick_set_pointer
//       Access: Private
//  Description: Sets up the internal pointer to the indicated row,
//               without first verifying that arrays haven't been
//               reallocated.
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexWriter::
quick_set_pointer(int row) {
  nassertv(has_column());

#if defined(_DEBUG)
  // Make sure we still have the same pointer as stored in the array.
  nassertv(_pointer_begin == _handle->get_write_pointer());
#endif

  _pointer = _pointer_begin + _packer->_column->get_start() + _stride * row;

#if defined(_DEBUG)
  // We have to allow the pointer to exceed the end by up to one row's
  // width, because the next call might be to add_data_*().
  nassertv(_pointer_begin == _pointer_end || (_pointer - _packer->_column->get_start())  <= _pointer_end);
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::inc_pointer
//       Access: Private
//  Description: Increments to the next row, and returns the data
//               pointer as it was before incrementing.
////////////////////////////////////////////////////////////////////
INLINE unsigned char *GeomVertexWriter::
inc_pointer() {
#if defined(_DEBUG)
  nassertr(_pointer < _pointer_end, empty_buffer);
  // Make sure we still have the same pointer as stored in the array.
  nassertr(_pointer_begin == _handle->get_write_pointer(), empty_buffer);
  nassertr(_pointer < _pointer_begin + _handle->get_data_size_bytes(), empty_buffer);
#endif

  unsigned char *orig_pointer = _pointer;
  _pointer += _stride;
  return orig_pointer;
}

////////////////////////////////////////////////////////////////////
//     Function: GeomVertexWriter::inc_add_pointer
//       Access: Private
//  Description: Increments to the next row, and returns the data
//               pointer as it was before incrementing.  If we are at
//               or past the end of data, implicitly adds more
//               rows first.
////////////////////////////////////////////////////////////////////
INLINE unsigned char *GeomVertexWriter::
inc_add_pointer() {
  if (_pointer >= _pointer_end) {
    // Reset the data pointer.
    int write_row = get_write_row();

    if (_vertex_data != (GeomVertexData *)NULL) {
      // If we have a whole GeomVertexData, we must set the length of
      // all its arrays at once.
      _handle = NULL;
      GeomVertexDataPipelineWriter writer(_vertex_data, true, _current_thread);
      writer.check_array_writers();
      writer.set_num_rows(max(write_row + 1, writer.get_num_rows()));
      _handle = writer.get_array_writer(_array);

    } else {
      // Otherwise, we can get away with modifying only the one array
      // we're using.
      _handle->set_num_rows(max(write_row + 1, _handle->get_num_rows()));
    }

    set_pointer(write_row);
  }
  return inc_pointer();
}

